package com.yellowninja.backup;

import com.jgoodies.forms.builder.PanelBuilder;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import com.yellowninja.backup.model.BackupTableModel;

import com.yellowninja.backup.ui.BackupDialog;
import com.yellowninja.backup.ui.StatusBar;

import com.yellowninja.backup.util.Backup;
import com.yellowninja.backup.util.BackupTask;
import com.yellowninja.backup.util.BackupUtils;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import java.text.ParseException;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

import org.jdesktop.jdic.tray.SystemTray;
import org.jdesktop.jdic.tray.TrayIcon;

import org.jdesktop.swingx.JXTable;

import org.jdom.JDOMException;

/**
 * @author Devin Gillman
 * 
 */
public class Main implements ActionListener, PropertyChangeListener {
	private Logger logger;

	private TrayIcon trayIcon;

	private JFrame mainFrame;

	private StatusBar sBar;

	private BackupDialog bDialog;

	private BackupTableModel backupModel;

	private JXTable backupTable;

	private JPopupMenu cellMenu;

	private JMenuItem runItem;

	private Vector<BackupTask> tasks;

	public Main() {
		System.loadLibrary("jdic");
		System.loadLibrary("tray");

		// Set the look and feel.
		try {
			UIManager
					.setLookAndFeel("org.jvnet.substance.skin.SubstanceBusinessBlueSteelLookAndFeel");
			JDialog.setDefaultLookAndFeelDecorated(true);
			JFrame.setDefaultLookAndFeelDecorated(true);
		} catch (Exception e) {
			// Do nothing.
		}

		// Set up the logging.
		try {
			logger = Logger.getRootLogger();
			logger.setLevel(Level.DEBUG);
			logger.addAppender(new FileAppender(new PatternLayout(
					"%-5p [%t]: %m%n"), System.getProperty("user.home")
					+ File.separator + ".yBackup" + File.separator + "log.txt",
					false));
			logger.debug("Created log file.");
		} catch (IOException ioe) {
			System.err.println(ioe.getLocalizedMessage());
			ioe.printStackTrace();
			System.exit(1);
		}

		// Load the configuration.
		try {
			// First check to see if the file exists.
			if (!(new File(System.getProperty("user.home") + File.separator
					+ ".yBackup")).exists()) {
				(new File(System.getProperty("user.home") + File.separator
						+ ".yBackup")).mkdir();

			}
			if (!(new File(System.getProperty("user.home") + File.separator
					+ ".yBackup" + File.separator + "config.xml")).exists()) {
				// Write a basic config file.
				try {
					BufferedWriter out = new BufferedWriter(new FileWriter(
							System.getProperty("user.home") + File.separator
									+ ".yBackup" + File.separator
									+ "config.xml"));
					out
							.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>");
					out.write("<config>");
					out.write("</config>");
					out.close();
				} catch (IOException ioe) {
					System.err.println(ioe.getLocalizedMessage());
					ioe.printStackTrace();
					System.exit(1);
				}

			}
			backupModel = new BackupTableModel(new XMLConfiguration(System
					.getProperty("user.home")
					+ File.separator
					+ ".yBackup"
					+ File.separator
					+ "config.xml"));
		} catch (ConfigurationException cex) {
			System.err.println(cex.getLocalizedMessage());
			cex.printStackTrace();
			System.exit(1);
		}

		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				// Create the JFrame.
				mainFrame = new JFrame("yBackup");
				mainFrame.setLayout(new BorderLayout());
				mainFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
				mainFrame.setIconImage((new ImageIcon(
						this.getClass().getResource(
								"/com/yellowninja/backup/images/clock.png")))
						.getImage());
				mainFrame.setJMenuBar(createMenuBar());

				CellConstraints cc = new CellConstraints();
				FormLayout layout = new FormLayout("f:p:g", "p, 3dlu, f:p:g");
				PanelBuilder panel = new PanelBuilder(layout);
				panel.setDefaultDialogBorder();

				// Layout the JFrame.
				panel.add(new JLabel("Backup Packages:"), cc.xy(1, 1));
				backupTable = new JXTable(backupModel);
				backupTable.setPreferredSize(new Dimension(450, 50));
				backupTable.setPreferredScrollableViewportSize(new Dimension(
						450, 50));
				backupTable.setColumnControlVisible(true);
				backupTable
						.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
				backupTable.addMouseListener(new MouseAdapter() {
					public void mousePressed(MouseEvent e) {
						if (SwingUtilities.isRightMouseButton(e)) {
							int row = backupTable.rowAtPoint(e.getPoint());
							if (row >= 0) {
								backupTable.setRowSelectionInterval(row, row);
								cellMenu.show(backupTable, e.getX(), e.getY());
							}
						}
					}
				});
				panel.add(new JScrollPane(backupTable), cc.xy(1, 3));
				mainFrame.add(panel.getPanel(), BorderLayout.CENTER);

				sBar = new StatusBar("Ready", null);
				mainFrame.add(sBar, BorderLayout.PAGE_END);

				mainFrame.pack();

				// Create the backup dialog.
				bDialog = new BackupDialog(mainFrame, backupModel
						.getBackupNames());
				bDialog.addComponentListener(new ComponentAdapter() {
					public void componentHidden(ComponentEvent e) {
						if (bDialog.getResult() == BackupDialog.OK) {
							if (bDialog.isEditing()) {
								backupModel.updateRow(bDialog.getBackup());
							} else {
								backupModel.addRow(bDialog.getBackup());
							}

							bDialog.clearDialog();
						}
					}
				});

				// Create the cellMenu.
				createCellMenu();

				// Create the System Tray Icon.
				trayIcon = new TrayIcon(new ImageIcon(
						this.getClass().getResource(
								"/com/yellowninja/backup/images/clock.png")),
						"yBackup", createTrayMenu());

				trayIcon.setIconAutoSize(true);
				trayIcon.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						mainFrame.setVisible(!mainFrame.isVisible());
					}
				});

				SystemTray tray = SystemTray.getDefaultSystemTray();
				tray.addTrayIcon(trayIcon);

				// Now check to see if we should run any backups at this point.
				tasks = new Vector<BackupTask>();
				try {
					for (int i = 0; i < backupModel.getRowCount(); i++) {
						if (BackupUtils.runBackup(backupModel.getBackup(i))) {
							logger.info("Running "
									+ backupModel.getBackup(i).getName()
									+ " because it is over due.");
							runBackup(backupModel.getBackup(i));
						} else {
							logger.info("Don't need to run "
									+ backupModel.getBackup(i).getName()
									+ " yet.");
						}
					}
				} catch (IOException ioe) {
					logger.error(ioe.getLocalizedMessage(), ioe);
				} catch (JDOMException jdome) {
					logger.error(jdome.getLocalizedMessage(), jdome);
				} catch (ParseException pe) {
					logger.error(pe.getLocalizedMessage(), pe);
				}
			}
		});
	}

	private void createCellMenu() {
		cellMenu = new JPopupMenu();

		JMenuItem addItem = new JMenuItem("New Backup", KeyEvent.VK_N);
		addItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_add.png")));
		addItem.getAccessibleContext().setAccessibleDescription(
				"Adds a new backup.");
		addItem.setActionCommand("ADD");
		addItem.addActionListener(this);
		cellMenu.add(addItem);
		JMenuItem editItem = new JMenuItem("Edit Backup", KeyEvent.VK_E);
		editItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_edit.png")));
		editItem.getAccessibleContext().setAccessibleDescription(
				"Edits the selected backup.");
		editItem.setActionCommand("EDIT");
		editItem.addActionListener(this);
		cellMenu.add(editItem);
		JMenuItem deleteItem = new JMenuItem("Delete Backup", KeyEvent.VK_D);
		deleteItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_delete.png")));
		deleteItem.getAccessibleContext().setAccessibleDescription(
				"Deletes the selected backup.");
		deleteItem.setActionCommand("DELETE");
		deleteItem.addActionListener(this);
		cellMenu.add(deleteItem);
		cellMenu.addSeparator();
		runItem = new JMenuItem("Run Backup", KeyEvent.VK_R);
		runItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_play.png")));
		runItem.getAccessibleContext().setAccessibleDescription(
				"Runs the selected backup.");
		runItem.setActionCommand("RUN");
		runItem.addActionListener(this);
		cellMenu.add(runItem);
	}

	private JMenuBar createMenuBar() {
		JMenuBar bar = new JMenuBar();

		JMenu fileMenu = new JMenu("File");
		bar.add(fileMenu);
		JMenuItem addItem = new JMenuItem("New Backup", KeyEvent.VK_N);
		addItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				ActionEvent.CTRL_MASK));
		addItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_add.png")));
		addItem.getAccessibleContext().setAccessibleDescription(
				"Adds a new backup.");
		addItem.setActionCommand("ADD");
		addItem.addActionListener(this);
		fileMenu.add(addItem);
		JMenuItem editItem = new JMenuItem("Edit Backup", KeyEvent.VK_E);
		editItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
				ActionEvent.CTRL_MASK));
		editItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_edit.png")));
		editItem.getAccessibleContext().setAccessibleDescription(
				"Edits the selected backup.");
		editItem.setActionCommand("EDIT");
		editItem.addActionListener(this);
		fileMenu.add(editItem);
		JMenuItem deleteItem = new JMenuItem("Delete Backup", KeyEvent.VK_D);
		deleteItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_delete.png")));
		deleteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D,
				ActionEvent.CTRL_MASK));
		deleteItem.getAccessibleContext().setAccessibleDescription(
				"Deletes the selected backup.");
		deleteItem.setActionCommand("DELETE");
		deleteItem.addActionListener(this);
		fileMenu.add(deleteItem);
		fileMenu.addSeparator();
		JMenuItem runItem = new JMenuItem("Run Backup", KeyEvent.VK_R);
		runItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
				ActionEvent.CTRL_MASK));
		runItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_play.png")));
		runItem.getAccessibleContext().setAccessibleDescription(
				"Runs the selected backup.");
		runItem.setActionCommand("RUN");
		runItem.addActionListener(this);
		fileMenu.add(runItem);
		fileMenu.addSeparator();
		JMenuItem exitItem = new JMenuItem("Exit", KeyEvent.VK_X);
		exitItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/cross.png")));
		exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
				ActionEvent.CTRL_MASK));
		exitItem.getAccessibleContext().setAccessibleDescription(
				"Exits yBackup.");
		exitItem.setActionCommand("EXIT");
		exitItem.addActionListener(this);
		fileMenu.add(exitItem);

		return bar;
	}

	private JPopupMenu createTrayMenu() {
		// Set up the System Tray Icon.
		JPopupMenu trayMenu = new JPopupMenu();

		JMenuItem addItem = new JMenuItem("New Backup", KeyEvent.VK_N);
		addItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				ActionEvent.CTRL_MASK));
		addItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_add.png")));
		addItem.getAccessibleContext().setAccessibleDescription(
				"Adds a new backup.");
		addItem.setActionCommand("ADD");
		addItem.addActionListener(this);
		trayMenu.add(addItem);
		JMenuItem editItem = new JMenuItem("Edit Backup", KeyEvent.VK_E);
		editItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
				ActionEvent.CTRL_MASK));
		editItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_edit.png")));
		editItem.getAccessibleContext().setAccessibleDescription(
				"Edits the selected backup.");
		editItem.setActionCommand("EDIT");
		editItem.addActionListener(this);
		trayMenu.add(editItem);
		JMenuItem deleteItem = new JMenuItem("Delete Backkup", KeyEvent.VK_D);
		deleteItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_delete.png")));
		deleteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D,
				ActionEvent.CTRL_MASK));
		deleteItem.getAccessibleContext().setAccessibleDescription(
				"Deletes the selected backup.");
		deleteItem.setActionCommand("DELETE");
		deleteItem.addActionListener(this);
		trayMenu.add(deleteItem);
		trayMenu.addSeparator();
		JMenuItem runItem = new JMenuItem("Run Backup", KeyEvent.VK_R);
		runItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
				ActionEvent.CTRL_MASK));
		runItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/clock_play.png")));
		runItem.getAccessibleContext().setAccessibleDescription(
				"Runs the selected backup.");
		runItem.setActionCommand("RUN");
		runItem.addActionListener(this);
		trayMenu.add(runItem);
		trayMenu.addSeparator();
		JMenuItem exitItem = new JMenuItem("Exit", KeyEvent.VK_X);
		exitItem.setIcon(new ImageIcon(this.getClass().getResource(
				"/com/yellowninja/backup/images/cross.png")));
		exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
				ActionEvent.CTRL_MASK));
		exitItem.getAccessibleContext().setAccessibleDescription(
				"Exits yBackup.");
		exitItem.setActionCommand("EXIT");
		exitItem.addActionListener(this);
		trayMenu.add(exitItem);

		return trayMenu;
	}

	private void runBackup(Backup backup) {
		sBar.setText("Beginning backup: " + backup.getName());
		BackupTask bTask = new BackupTask(backup) {
			public void finished() {
				tasks.remove(this.getBackup());
				// Check to see if the selected back-up is this one.
				if (backupTable.getSelectedRow() == this.getBackup().getID()) {
					runItem.setText("Run Backup");
					runItem.setAccelerator(KeyStroke.getKeyStroke(
							KeyEvent.VK_R, KeyEvent.CTRL_MASK));
					runItem.setIcon(new ImageIcon(this.getClass().getResource(
							"/com/yellowninja/backup/images/clock_play.png")));
					runItem.getAccessibleContext().setAccessibleDescription(
							"Runs the selected backup.");
					runItem.setActionCommand("RUN");
				}
			}
		};
		bTask.addPropertyChangeListener(this);
		bTask.start();
		tasks.add(bTask);
	}

	public void actionPerformed(ActionEvent ae) {
		if (ae.getActionCommand().equals("EXIT")) {
			// Save the configuration.
			try {
				backupModel.saveModel();
			} catch (ConfigurationException ce) {
				logger.error(ce.getLocalizedMessage(), ce);
			}
			logger.debug("Exited cleanly.");
			System.exit(0);
		} else if (ae.getActionCommand().equals("ADD")) {
			bDialog.setVisible(true);
		} else if (ae.getActionCommand().equals("DELETE")) {
			if (backupTable.getSelectedRow() >= 0) {
				int result = JOptionPane
						.showConfirmDialog(mainFrame,
								"Are you sure that you want to delete backup "
										+ backupModel.getBackup(
												backupTable.getSelectedRow())
												.getName() + "?",
								"Confirm Delete", JOptionPane.YES_NO_OPTION,
								JOptionPane.QUESTION_MESSAGE);
				if (result == JOptionPane.YES_OPTION) {
					backupModel.removeRow(backupTable.getSelectedRow());
				}
			} else {
				JOptionPane.showMessageDialog(mainFrame,
						"No Backup was selected to delete.", "No Selection",
						JOptionPane.INFORMATION_MESSAGE);
			}

		} else if (ae.getActionCommand().equals("EDIT")) {
			if (backupTable.getSelectedRow() >= 0) {
				bDialog.setBackup(backupModel.getBackup(backupTable
						.getSelectedRow()));
				bDialog.setEditing(true);
				bDialog.setVisible(true);
			} else {
				JOptionPane.showMessageDialog(mainFrame,
						"No Backup was selected to edit.", "No Selection",
						JOptionPane.INFORMATION_MESSAGE);
			}
		} else if (ae.getActionCommand().equals("RUN")) {
			if (backupTable.getSelectedRow() >= 0) {
				runBackup(backupModel.getBackup(backupTable.getSelectedRow()));
			} else {
				JOptionPane.showMessageDialog(mainFrame,
						"No Backup was selected to run.", "No Selection",
						JOptionPane.INFORMATION_MESSAGE);
			}
		}
	}

	public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
		BackupTask bTask = (BackupTask) propertyChangeEvent.getSource();
		String property = propertyChangeEvent.getPropertyName();
		if (property.equals("currentFile")) {
			String currentFile = (String) propertyChangeEvent.getNewValue();
			sBar.setText(bTask.getBackup().getName() + ": " + currentFile);
		} else if (property.equals("done")) {
			if ((Boolean) propertyChangeEvent.getNewValue()) {
				Toolkit.getDefaultToolkit().beep();
				sBar.setText(bTask.getBackup().getName() + " is done.");
				trayIcon.displayMessage("Backup Complete", bTask.getBackup()
						.getName()
						+ " is done.", 0);
				backupModel.updateRow(bTask.getBackup());
			}
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Main();
	}

}
